import socket
from select import select, poll, POLLIN, POLLOUT

send_queue = {}

def start_tcp_server_with_poll(address, port):
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server.bind((address, port))
    server.listen(10)
    clients = {}
    p = poll()
    p.register(server.fileno(),POLLIN)

    while True:
        ready_fds = p.poll()

        for fd,ev in ready_fds:
            if ev & POLLIN:
                if fd == server.fileno():
                    client,addr = server.accept()
                    clients[client.fileno()] = client

                    for c in clients.keys():

                        if c not in send_queue:
                            send_queue[c] = []
                        data_queue = send_queue[c]
                        data_queue.append(("{} is online now".format(addr)).encode("utf-8"))

                        p.register(c,POLLOUT|POLLIN)
                    # p.register(client.fileno(),POLLIN|POLLOUT)
                    print("{} is online".format(addr))
                else:
                    client = clients[fd]
                    try:
                        data = client.recv(65535)
                        if not data:
                            raise ConnectionError

                        try:

                            print('{} sent a message:{}'.format(client.getpeername(), data.decode("utf-8")))
                        except:
                            print('data error')
                            pass
                        for c in clients.keys():
                            if c != client.fileno() and c != server.fileno():
                                if c not in send_queue:
                                    send_queue[c] = []
                                data_queue = send_queue[c]
                                data_queue.append(data)
                                p.register(c,POLLIN|POLLOUT)

                    except ConnectionError:
                        client.close()
                        p.unregister(fd)

            if ev & POLLOUT:
                if fd not in send_queue:
                    p.register(fd,POLLIN)
                    # p.unregister(fd)
                    continue

                data_queue = send_queue[fd]
                data = data_queue.pop(0)
                client = clients[fd]
                client.send(data)
                if not data_queue:
                    del send_queue[fd]
                    p.register(fd, POLLIN)











def start_udp_server(address, port):
    pass





def start_tcp_server(address, port):
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

    server.bind((address, port))
    server.listen(10)
    read_list = [server]
    write_list = []

    while True:
        read_fds, write_fds, _ = select(read_list, write_list, [])
        for fd in read_fds:
            assert isinstance(fd, socket.socket)
            if fd == server:
                client, addr = fd.accept()
                for r in read_list:
                    if r != server:
                        if r not in send_queue:
                            send_queue[r] = []
                        # r.send(('{} is on the line'.format(addr)).encode('utf-8'))
                        data_queue = send_queue[r]
                        data_queue.append(("{} is online now".format(addr)).encode("utf-8"))
                        if r not in write_list:
                            write_list.append(r)
                read_list.append(client)
                print("{} is online".format(addr))
            else:
                try:
                    data = fd.recv(65535)
                    if not data:
                        raise ConnectionError
                    try:
                        print("{} send a meesage {}".format(fd.getpeername(), data.decode('utf-8')))
                    except:
                        pass
                    for r in read_list:
                        if r != server and r != fd:
                            sdata = "{} send a message {}".format(fd.getpeername(), data.decode('utf-8'))
                            r.send(sdata.encode('utf-8'))
                except ConnectionError:
                    read_list.remove(fd)
                    fd.close()

        for fd in write_fds:
            if fd not in write_list:
                write_list.remove(fd)
                continue
            data_queue = send_queue[fd]
            data = data_queue.pop(0)
            fd.send(data)
            if not data_queue:
                del send_queue[fd]
                write_list.remove(fd)


import argparse
import sys

ap = argparse.ArgumentParser()

ap.add_argument("-t", type=str, choices=('tcp', 'udp'), help='use udp/tcp', default='tcp')
ap.add_argument('-H', type=str, required=True, help='ip address to bind')
ap.add_argument('-p', type=int, required=True, help='port to bind')

arg = ap.parse_args(sys.argv[1:])

if arg.t == 'tcp':
    start_tcp_server_with_poll(arg.H, arg.p)
else:
    start_tcp_server(arg.host, arg.port)
